package aor2.cpu.blockregisters;

import aor2.components.Element;
import aor2.components.ElementIO;
import aor2.components.InstructionCode;
import aor2.components.Register;
import aor2.components.Restorable;
import aor2.components.Signal;
import aor2.cpu.InternBus;
import aor2.cpu.blockcontrol.ConditionSignalSet;
import aor2.cpu.blockcontrol.ControlSignalSet;
import aor2.cpu.blockcontrol.ControlUnitMicroProg;

public enum InstructionRegister implements Element, ElementIO, Restorable {
	IR0(0, InternBus.M1, InternBus.M2, true), IR1(0, InternBus.M1,
			InternBus.M2, false), IR2(0, InternBus.M1, InternBus.M2, false);

	static {
		ControlSignalSet css = ControlSignalSet.getInstance();
		css.get("IR0in").addDestinationElementIO(IR0);
		css.get("IR1in").addDestinationElementIO(IR1);
		css.get("IR2in").addDestinationElementIO(IR2);
		css.get("IR0out").addSourceElementIO(IR0);
		css.get("IR1out").addSourceElementIO(IR1);
		css.get("IR2out").addSourceElementIO(IR2);
		css.get("IR0Lout").addSourceElementIO(IR0);
	}

	private static ControlUnitMicroProg cu = null;

	public static ControlUnitMicroProg getCu() {
		return cu;
	}

	public static void setCu(ControlUnitMicroProg cu) {
		InstructionRegister.cu = cu;
	}

	private Register reg;
	private boolean generator;

	@Override
	public long getState() {
		return reg.getState();
	}

	@Override
	public void setState(long state) {
		reg.setState(state);
	}

	public void out() {
		if (generator) {// IR0Lout, IROout ne postoji
			InternBus.M2.setState(reg.getState() & 0xFF);
		} else
			reg.out();
	}

	/**
	 * instrukcija u kojoj je IR0in treba da generise izlaze KMOP KMADR1/2
	 */
	public void in() {
		reg.in();
		if (generator) {
			// When IR0in, condition signals need to be updated
			ConditionSignalSet set = ConditionSignalSet.getInstance();
			clearAdrSignal();
			InstructionSignal.setActive();
			long ir0 = InstructionRegister.IR0.getState();

			/*
			 * adr1
			 */
			long adr1L = ir0 & InstructionCode.adr1mask;
			switch ((int) adr1L) {
			case (int) InstructionCode.regdir1: {
				set.get("regdir1").setActive(true);
				break;
			}
			case (int) (InstructionCode.postinc1): {
				set.get("postinc1").setActive(true);
				break;
			}
			case (int) (InstructionCode.predec1): {
				set.get("predec1").setActive(true);
				break;
			}
			case (int) (InstructionCode.index1): {
				set.get("index1").setActive(true);
				break;
			}
			case (int) (InstructionCode.regind1): {
				set.get("regind1").setActive(true);
				break;
			}
			case (int) (InstructionCode.postincind1): {
				set.get("postincind1").setActive(true);
				break;
			}
			case (int) (InstructionCode.predecind1): {
				set.get("predecind1").setActive(true);
				break;
			}
			case (int) (InstructionCode.indexind1): {
				set.get("indexind1").setActive(true);
				break;
			}
			}

			/*
			 * adr2
			 */
			long adr2L = ir0 & InstructionCode.adr2mask;
			switch ((int) adr2L) {
			case (int) InstructionCode.regdir2: {
				set.get("regdir2").setActive(true);
				break;
			}
			case (int) (InstructionCode.postinc2): {
				set.get("postinc2").setActive(true);
				if ((ir0 & InstructionCode.reg2mask) == 7L)// PC
					set.get("immed2").setActive(true);
				break;
			}
			case (int) (InstructionCode.predec2): {
				set.get("predec2").setActive(true);
				break;
			}
			case (int) (InstructionCode.index2): {
				set.get("index2").setActive(true);
				break;
			}
			case (int) (InstructionCode.regind2): {
				set.get("regind2").setActive(true);
				break;
			}
			case (int) (InstructionCode.postincind2): {
				set.get("postincind2").setActive(true);
				break;
			}
			case (int) (InstructionCode.predecind2): {
				set.get("predecind2").setActive(true);
				break;
			}
			case (int) (InstructionCode.indexind2): {
				set.get("indexind2").setActive(true);
				break;
			}
			}

			if (cu != null)
				cu.generateKMsignals();

			set.get("adr0").setActive(
					InstructionSignal.RTI.isActive()
							|| InstructionSignal.RTS.isActive()
							|| InstructionSignal.TRPE.isActive()
							|| InstructionSignal.INT.isActive()
							|| InstructionSignal.TRPD.isActive()
							|| InstructionSignal.INTE.isActive()
							|| InstructionSignal.INTD.isActive()
							|| InstructionSignal.BNZ.isActive()
							|| InstructionSignal.BRN.isActive()
							|| InstructionSignal.BSR.isActive());
			set.get("adr1").setActive(
					InstructionSignal.INC.isActive()
							|| InstructionSignal.DEC.isActive()
							|| InstructionSignal.NOT.isActive()
							|| InstructionSignal.ASR.isActive()
							|| InstructionSignal.JMPIND.isActive()
							|| InstructionSignal.JSRIND.isActive());
			set.get("notpom2")
					.setActive(
							!((set.get("index2").isActive() || set.get(
									"indexind2").isActive()) && set.get("adr0")
									.isActive() == false));
			set.get("notpom1")
					.setActive(
							!((set.get("index1").isActive() || set.get(
									"indexind1").isActive()) && (set
									.get("adr0").isActive() == false && set
									.get("adr1").isActive() == false)));
		}
	}

	private InstructionRegister(long state, Element input, Element output,
			boolean generateSignals) {
		this.reg = new Register(state, input, output);
		generator = generateSignals;
	}

	public static String info() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("IR0[" + IR0.getState() + "]\n");
		buffer.append("IR1[" + IR1.getState() + "]\n");
		buffer.append("IR2[" + IR2.getState() + "]");
		return buffer.toString();
	}

	private void clearAdrSignal() {
		ConditionSignalSet set = ConditionSignalSet.getInstance();
		Signal s;
		s = set.get("regdir1");
		s.setActive(false);
		s = set.get("postinc1");
		s.setActive(false);
		s = set.get("predec1");
		s.setActive(false);
		s = set.get("index1");
		s.setActive(false);
		s = set.get("regind1");
		s.setActive(false);
		s = set.get("predecind1");
		s.setActive(false);
		s = set.get("postincind1");
		s.setActive(false);
		s = set.get("indexind1");
		s.setActive(false);

		s = set.get("regdir2");
		s.setActive(false);
		s = set.get("postinc2");
		s.setActive(false);
		s = set.get("predec2");
		s.setActive(false);
		s = set.get("index2");
		s.setActive(false);
		s = set.get("regind2");
		s.setActive(false);
		s = set.get("predecind2");
		s.setActive(false);
		s = set.get("postincind2");
		s.setActive(false);
		s = set.get("indexind2");
		s.setActive(false);
		s = set.get("immed2");
		s.setActive(false);

	}

	@Override
	public void restore(String restorePoint) {
		String trim = restorePoint.replace("<IR>", "");
		trim = trim.replace("</IR>", "");
		trim = trim.trim();
		String[] states = trim.split(" ");

		IR0.reg.setState(Long.parseLong(states[0]));
		IR1.reg.setState(Long.parseLong(states[1]));
		IR2.reg.setState(Long.parseLong(states[2]));

	}

	@Override
	public String createRestorePoint() {

		return "<IR>\n\t" + IR0.reg.getState() + " " + IR1.reg.getState() + " "
				+ IR2.reg.getState() + "\n</IR>";
	}

	public static void main(String[] args) {
		String restorePoint = InstructionRegister.IR0.createRestorePoint();

		System.out.println(restorePoint);

		InstructionRegister.IR0.restore(restorePoint);
	}

	@Override
	public String beginTag() {
		return "<IR>";
	}

	@Override
	public String endTag() {
		return "</IR>";
	}
}
